﻿/*
 * Name:        Utilities
 * Description: A set of tools, to make everything faster
 * 
 * Contains:
 *      Constants       - Constant variables for usage everywhere
 *      EventList       - Contains a list of all events
 *      PrecachedSprite - Sprite that acts as a container for the sprite, holds sprite image, bitmap, also applies colour keying
 *      Vector2d        - 2d vector that can be used as a coordinate, or a vector
 *      Dimensions2d    - Two 2d vectors to act as dimensions
 *      BitWiseOperator - Adds, removes, toggles and checks if bit array has specified bit
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.IO;

namespace Gyvate.Util
{
    internal class Constants
    {
        public static Color COLOUR_KEY = Color.FromArgb(255, 0, 255);
        public static Color COLOUR_SPOTLIGHT = Color.FromArgb(10, 0, 30, 50);

        public static int EASY_SPEED = 300;
        public static int EASY_SPOTLIGHT = 4096;
        public static float EASY_SPOTLIGHT_SHRINK_FACTOR = 0.95f;
        public static int EASY_SCORE_DIVIDER = 500;
        public static int EASY_SPEED_UP = 4;

        public static int NORMAL_SPEED = 250;
        public static int NORMAL_SPOTLIGHT = 4096;
        public static float NORMAL_SPOTLIGHT_SHRINK_FACTOR = 0.85f;
        public static int NORMAL_SCORE_DIVIDER = 800;
        public static int NORMAL_SPEED_UP = 6;

        public static int HARD_SPEED = 200;
        public static int HARD_SPOTLIGHT = 4096;
        public static float HARD_SPOTLIGHT_SHRINK_FACTOR = 0.75f;
        public static int HARD_SCORE_DIVIDER = 1000;
        public static int HARD_SPEED_UP = 10;

        public static int SPOTLIGHT_MIN_SIZE = 200;

        public static int SPEED_MIN = 10;

        public static int FPS = 60;

        public static int SPRITE_SIZE = 16;
        public static string SPRITE_HEAD = "head.png";
        public static string SPRITE_BODY = "body.png";
        public static string SPRITE_BACKGROUND = "background.png";
        public static string SPRITE_WALL = "wall.png";
        public static string SPRITE_FOOD = "food.png";
        public static string SPRITE_BLOCKED = "blocked.png";
        public static string SPRITE_SPAWN = "spawn.png";
        public static string SPRITE_SPAWN_90 = "spawn90.png";
        public static string SPRITE_SPAWN_180 = "spawn180.png";
        public static string SPRITE_SPAWN_270 = "spawn270.png";

        public static int FIELD_WIDTH = 38;
        public static int FIELD_HEIGHT = 27;

        public static int HASH_CODE = 13;
        public static int HASH_SALT = 7;

        public static int UI_WIDTH = FIELD_WIDTH + 1;
        public static int UI_HEIGHT = FIELD_HEIGHT + 11;
        public static int UI_MM_NUM_ITEMS = 5;

        public static string MAP_FORMAT = "Map File|*.xml";
        public static int MAP_VERSION = 1;

        public static string SOUND_EAT = "eat.wav";
        public static string SOUND_CLICK = "click.wav";
        public static string SOUND_LOOSE = "loose.wav";
        public static string SOUND_WIN = "win.wav";
        public static string SOUND_RESTART = "restart.wav";

        public static string HELP_FILE = "Snake.chm";
    }

    public enum TileList
    {
        TILE_NONE = 0,
        TILE_WALL,
        TILE_FOOD,
        TILE_SNAKESPAWN,
        TILE_SNAKESPAWN_90,
        TILE_SNAKESPAWN_180,
        TILE_SNAKESPAWN_270,
        MAX_TILES
    }

    public enum EventList
    {
        EVENT_UNKNOWN = 0,
        EVENT_NEWGAME,
        EVENT_GAMEOVER,
        EVENT_PAUSE,
        EVENT_EATEN,
        EVENT_COLLIDEWORLD,
        EVENT_WIN,
        EVENT_COLLIDEWALL
    }

    public enum Difficulty
    {
        DIFFICULTY_EASY = 0,
        DIFFICULTY_NORMAL,
        DIFFICULTY_HARD
    }

    internal class PrecachedSprite
    {
        private Image m_sprite;
        private Bitmap m_bitmap;
        private bool m_bPrecached;

        public Image GetImage() { return m_sprite; }        //Returns image
        public Bitmap GetBitmap() { return m_bitmap; }      //Returns bitmap
        public bool IsPrecached() { return m_bPrecached; }  //Returns precache state

        /*
         * Constructor takes file path as a parameter
         */
        public PrecachedSprite(string sprite)
        {
            m_sprite = null;
            m_bitmap = null;
            m_bPrecached = false;
            
            if (!File.Exists(sprite))
                return;

            m_sprite = Image.FromFile(sprite);

            m_bitmap = new Bitmap(m_sprite);

            m_bPrecached = true;

            //Apply colour key
            for (int y = 0; y < m_sprite.Height; y++)
                for (int x = 0; x < m_sprite.Width; x++)
                    if (Constants.COLOUR_KEY == m_bitmap.GetPixel(x, y))
                        m_bitmap.SetPixel(x, y, Color.Transparent);
        }
    }

    public class Vector2d
    {
        public int x, y;

        /*
         * Constructor that makes a vector from x and y
         */
        public Vector2d(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        /*
         * Constructor that makes an empty vector
         */
        public Vector2d()
        {
            this.x = this.y = 0;
        }

        /*
         * Constructor that makes a copy from an already existing vector
         */
        public Vector2d(Vector2d cp)
        {
            this.x = cp.x;
            this.y = cp.y;
        }

        public static Vector2d operator -(Vector2d vec1, Vector2d vec2)
        {
            return new Vector2d(vec1.x - vec2.x, vec1.y - vec2.y);
        }

        public static Vector2d operator +(Vector2d vec1, Vector2d vec2)
        {
            return new Vector2d(vec1.x + vec2.x, vec1.y + vec2.y);
        }

        public static Vector2d operator *(Vector2d vec, int scalar)
        {
            return new Vector2d(vec.x * scalar, vec.y * scalar);
        }

        public static Vector2d operator /(Vector2d vec, int scalar)
        {
            if (scalar == 0)
                return null;

            return new Vector2d((int)(vec.x / scalar), (int)(vec.y / scalar));
        }

        public static bool operator ==(Vector2d vec1, Vector2d vec2)
        {
            return (vec1.x == vec2.x && vec1.y == vec2.y);
        }

        public static bool operator !=(Vector2d vec1, Vector2d vec2)
        {
            return !(vec1 == vec2);
        }

        public override bool Equals(object obj)
        {
            if (obj.GetType() != typeof(Vector2d))
                return false;

            Vector2d comp = (Vector2d)obj;

            return (this.x == comp.x && this.y == comp.y);
        }

        public override int GetHashCode()
        {
            int hash = Constants.HASH_CODE;
            hash = (hash * Constants.HASH_SALT) + x.GetHashCode();
            hash = (hash * Constants.HASH_SALT) + y.GetHashCode();

            return hash;
        }

        public void Normalize()
        {
            double len = Math.Sqrt((this.x * this.x) + (this.y * this.y));

            if (len == 0.00)
                return;

            len = Math.Abs(len);

            double x = this.x / len;
            double y = this.y / len;

            this.x = (int)Math.Round(x);
            this.y = (int)Math.Round(y);
        }
    }

    public class Dimensions2d
    {
        public Vector2d size;
        public Vector2d pos;

        /*
         * Null dimensions constructor
         */
        public Dimensions2d()
        {
            size = new Vector2d();
            pos = new Vector2d();
        }

        /*
         * Proper constructor
         */
        public Dimensions2d(Vector2d pos, Vector2d size)
        {
            this.size = size;
            this.pos = pos;
        }

        /*
         * Constructor from 4 ints
         */
        public Dimensions2d(int x, int y, int w, int h)
        {
            pos = new Vector2d(x, y);
            size = new Vector2d(w, h);
        }
    }

    public class BitWiseOperator
    {
        /*
         * Adds a bit
         * Parameters:
         *      bitArray - reference to bit array of which we are changing
         *      bit      - bit that we a re changing
         */
        public static void AddBit(ref int bitArray, int bit)
        {
            bitArray |= 1 << bit;
        }

        /*
         * Removes a bit
         * Parameters:
         *      bitArray - reference to bit array of which we are changing
         *      bit      - bit that we are changing
         */
        public static void RemoveBit(ref int bitArray, int bit)
        {
            bitArray &= ~(1 << bit);
        }

        /*
         * Toggles a bit
         * Parameters:
         *      bitArray - reference to bit array of which we are changing
         *      bit      - bit that we are changing
         */
        public static void ToggleBit(ref int bitArray, int bit)
        {
            bitArray ^= 1 << bit;
        }

        /*
         * Clears bit array
         */
        public static void Clear(ref int bitArray)
        {
            bitArray = 0;
        }

        /*
         * Checks if array has a bit
         * Parameters:
         *      bitArray - bit we are checking
         *      bit      - bit that we are changing
         * Returns:
         *     true if has bit, false if doesn't
         */
        public static bool HasBit(int bitArray, int bit)
        {
            int bitResult = bitArray & (1 << bit);
            return bitResult > 0 ? true : false;
        }
    }
}
